package org.sparkle.app;

import groovy.lang.GroovyShell;
import groovy.util.GroovyScriptEngine;
import groovy.util.ResourceException;

import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;

import javax.script.CompiledScript;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.codehaus.groovy.jsr223.GroovyCompiledScript;
import org.codehaus.groovy.jsr223.GroovyScriptEngineImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sparkle.cache.AppSession;
import org.sparkle.html.api.AbstractHtmlElement;
import org.sparkle.html.api.Listener;
import org.sparkle.html.impl.basic.Form;
import org.sparkle.html.impl.basic.Form.CustomFormBean;
import org.sparkle.html.impl.basic.Script;
import org.sparkle.html.impl.js.state.ElementState;
import org.sparkle.html.impl.js.state.HtmlState;
import org.sparkle.web.filter.RouteFilter;
import org.sparkle.web.page.AbstractPage;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.json.JsonHierarchicalStreamDriver;
import com.thoughtworks.xstream.io.json.JsonWriter;

public abstract class AbstractApplication implements Serializable {

	private static final long serialVersionUID = 208602000186261090L;
	
	//private Map<String, AbstractPage> pages;
	private static GroovyScriptEngine groovyScriptEngine;
	private String companyName;
	private static XStream xstreamJson;
	private static Map<String, Class<AbstractPage>> groovyClass;

	static{
		String[] roots = new String[] {
				System.getProperty("catalina.base") + "/webapps/sparkle/pages",				
				System.getProperty("catalina.base") + "/webapps/sparkle/template/company"				
				};
		try {
			setGroovyScriptEngine(new GroovyScriptEngine(roots));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		groovyClass=new HashMap<String,Class<AbstractPage>>();
	}
	
	public AbstractApplication(String _companyName) {
		this.companyName=_companyName;		
	}

	public void route(HttpServletRequest request, HttpServletResponse response, AppSession appSession) {
		Logger log=LoggerFactory.getLogger(RouteFilter.class);
		
		String path=request.getRequestURI().replaceFirst(request.getContextPath()+"/portal", "");
		
		if(path!=null && !path.startsWith("/"))
			path="/"+path;
		
		if(path.startsWith("/actions")){
			handleActions(request,response,appSession);
			return;
		}
		
		if("/".equals(path.trim())){
			path+="Init";
		}
		
		path=getCompanyName()+path+".groovy";
		log.debug("Request URI: "+path);
		
		ServletOutputStream out=null;
		
		String method=request.getParameter("method")!=null?request.getParameter("method"):"renderInt";
		
		try {
			out=response.getOutputStream();
			Object obj=renderGroovyScript(method,path, appSession);
			if(obj!=null)				
				out.write(obj.toString().getBytes());
		} catch (IOException e) {
			if(out!=null)
				try {
					out.write(e.getMessage().getBytes());
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			e.printStackTrace();
		}
	}

	private void handleActions(HttpServletRequest request,
			HttpServletResponse response, AppSession appSession) {
		String listenerid = request.getParameter("listenerid");
		String beanid = request.getParameter("beanid");
		
		Form.CustomFormBean bean = null;
		try {
			if (beanid != null && beanid.trim().length() > 0) {
				bean = new Form.BeanFactory().getBean(
						request.getParameterMap(),
						(CustomFormBean) this.getClass().getClassLoader()
								.loadClass(beanid).newInstance());
			}
		} catch (Exception e1) {
			e1.printStackTrace();
		}
		
		if(listenerid!=null && appSession.getObserver().getListener(listenerid)!=null){
			Listener l=appSession.getObserver().getListener(listenerid);
			l.run(request, bean);
		}
		
		response.setContentType("application/json");
		
		try {
			response.getOutputStream().print(getXstreamJson().toXML(appSession.getAppState().getHtmlState().getElstates()));
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public static GroovyScriptEngine getGroovyScriptEngine() {
		return groovyScriptEngine;
	}

	public static void setGroovyScriptEngine(GroovyScriptEngine groovyScriptEngine) {
		AbstractApplication.groovyScriptEngine = groovyScriptEngine;
	}

	public String getCompanyName() {
		return companyName;
	}

	public void setCompanyName(String companyName) {
		this.companyName = companyName;
	}

	@SuppressWarnings("unchecked")
	public Object renderGroovyScript(String method,String scriptName, AppSession appSession){		 
		try {
			Class<AbstractPage> scriptClass=null;
			
			scriptClass=groovyClass.get(scriptName);
			
			if(scriptClass==null){
			
				try{
					
	//				File f=new File(scriptName);
	//				groovy.lang.Script s=new GroovyShell().parse(f);
	//				CompiledScript cs = new GroovyCompiledScript(new GroovyScriptEngineImpl(), s.getClass());
	//				
	//				cs.eval()
					
					
					
					scriptClass = getGroovyScriptEngine().loadScriptByName(scriptName);
				}catch(ResourceException re){
					
				}
				
				if(scriptClass==null){
					try{
						scriptClass = getGroovyScriptEngine().loadScriptByName(scriptName.replaceFirst(getCompanyName()+"/", ""));
					}catch(ResourceException re){
						//provo a cercare una cartella con questo nome
						scriptClass = getGroovyScriptEngine().loadScriptByName(scriptName.replaceFirst(getCompanyName()+"/", "").replaceFirst(".groovy", "/Init.groovy"));
					}				
				}
				
				groovyClass.put(scriptName, scriptClass);
			
			}
			
			AbstractPage scriptInstance = (AbstractPage) scriptClass.newInstance();
			
			appSession.getGroovyScript().put(scriptName, scriptInstance);
			
			Object result = null;

			result = scriptClass.getMethod(method,
					new Class[] {}).invoke(scriptInstance, new Object[] {});

			if(result instanceof AbstractHtmlElement){
				
				if(appSession.getAppState().getScriptElement()!=null && appSession.getAppState().getScriptElement().size()>0)
					((AbstractHtmlElement)result).append(new Script(appSession.getAppState().getScriptElement()));
				return ((AbstractHtmlElement)result).render();
			}
			
			return result;
			
		} catch (Exception e) {
			e.printStackTrace();
			return e.getClass().getName()+" -> "+e.getMessage();
		}
	}

	
	public static XStream getXstreamJson() {
		
		if(xstreamJson==null){
			xstreamJson = new XStream(new JsonHierarchicalStreamDriver() {
	
				@Override
				public HierarchicalStreamWriter createWriter(Writer out) {			
					return new JsonWriter(out, JsonWriter.DROP_ROOT_MODE);
				}
	
			});
			xstreamJson.setMode(XStream.NO_REFERENCES);
			xstreamJson.alias("htmlstate", HtmlState.class);
			xstreamJson.alias("elementstate", ElementState.class);
		}
		
		return xstreamJson;
	}
}
